package cmp.viewer;

/*****************************************************
 * viewMultiTCW and runMultiTCW frames
 */
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.net.InetAddress;
import java.net.URL;
import java.sql.ResultSet;
import java.util.Vector;
import java.util.prefs.Preferences;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextField;

import cmp.compile.CompileMain;
import cmp.compile.panels.CompilePanel;
import assem.DBConn;
import cmp.database.DBinfo;
import cmp.main.Globals;
import cmp.viewer.panels.AlignmentViewPanel;
import cmp.viewer.panels.DatabaseSelectPanel;
import cmp.viewer.panels.ListDataPanel;
import cmp.viewer.panels.MemberQueryPanel;
import cmp.viewer.panels.MenuPanel;
import cmp.viewer.panels.POGQueryPanel;
import cmp.viewer.panels.ResultPanel;
import cmp.viewer.panels.TextPanel;

import util.Debug.ErrorReport;
import util.methods.HostsCfg;
import util.methods.UIHelpers;
import util.methods.Version;
import util.ui.TCWEditorPane;

public class ViewerFrame extends JFrame {
	//Menus are defined here.. all references should be from here as well
	public static final String [] MAIN_MENU = { 
		">Instructions", ">Overview", ">All Clusters", ">All Sequences", ">Filter Clusters", 
		">Filter Sequences", ">Results" };
	public static final String [] MENU_DESCRIP = { 
		"Basic instructions for using multiTCW", "Information about the database", 
		"A list of all groups", "A list of all members", 
		"Filter out groups", "Filter out members", "all query results" };

	public static final int PAIR_MODE = 0;
	public static final int MULTI_MODE = 1;

	private static final String [] RESULT_COLUMNS = { "Name", "Summary" };

	private static final long serialVersionUID = -4137848844345115643L;

	private static Vector<ViewerFrame> openFrames = null;
	
	/***************************************
	 * runMultiTCW -- called from CompileMain
	 */
	public ViewerFrame(CompileMain parent) {
		theParent = parent;
		theSettings = new ViewerSettings(this);
		getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.PAGE_AXIS));
		setWindowSettings("cmpcompileviewer");
		setBlast();
		showCompileSelection();
	}
	
	public CompileMain getParentMain() { return theParent; }
	
	private void showCompileSelection() {
		Vector<String> hostNames = new Vector<String>();
		hostNames.add(CompilePanel.hosts.host);
		
		try {
			setTitle("runMultiTCW " + Version.strPAVEver + " on " + 
				InetAddress.getLocalHost().getHostName());
		} catch (Exception e) {
			setTitle("runMultiTCW " + Version.strPAVEver );
		}

		setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		
		compilePanel = new CompilePanel(this, hostNames);
		addComponentListener(new ComponentAdapter() {
			public void componentResized(ComponentEvent e) {
				compilePanel.resizedEvent(e);
			}
		});
		pack();
	}
	
	public CompilePanel getCompilePanel() {
		if (compilePanel == null) {
			ErrorReport.die("Creating the main panel");
		}
		return compilePanel;
	}
	/***************************************************
	 * viewMultiTCW
	 */
	public ViewerFrame() {
		openFrames = new Vector<ViewerFrame> ();
		openFrames.add(this);
		ErrorReport.setErrorReportFileName(Globals.CmpErrorLog);
		theSettings = new ViewerSettings(this);
		setWindowSettings("cmpselect");
		setBlast();
		buildSelectFrame();
	}

	//Called when the user types in the name of the database
	public ViewerFrame(String dbName) {
		ErrorReport.setErrorReportFileName(Globals.CmpErrorLog);
		HostsCfg hosts = new HostsCfg();

		try {
			strHostName = hosts.host;//java.net.InetAddress.getLocalHost().getHostName();
			strDBUser = hosts.user;//userData[0];
			strDBPass = hosts.pass;//userData[1];			

			if(!dbName.startsWith(Globals.CMPDBPREFIX + "_"))
				dbName = Globals.CMPDBPREFIX + "_" + dbName;
			strDBName = dbName;
			System.err.println("Opening " + strDBName + " multiTCW version " 
					+ Globals.VERSION + " " + Globals.VDATE + " (host " + strHostName + ")");
	
			if(!hosts.checkDBConnect(strDBName)) 
			{
				System.out.println("Could not connect to database " + strDBName + " on host " + hosts.host + ", exiting. ");
				System.exit(0);
			}

			theSettings = new ViewerSettings(this);
			setBlast();
			
			setTitle("viewMultiTCW " + Globals.VERSION + " : " + dbName);
			setWindowSettings("multiTCW");
			buildFrame();
		}
		catch(Exception e) {
			ErrorReport.prtReport(e, "Error finding server name");
		}
		catch(Error e) {
			ErrorReport.reportFatalError(e, "Fatal error finding server name", null);
		}
	}
	
	public ViewerFrame(String host, String dbName, boolean isApplet, String dbUser, String dbPass) {
		if(openFrames != null) {
			openFrames.add(this);
		}
		
		bIsApplet = isApplet;
		UIHelpers.setIsApplet(isApplet);
		
		if (!isApplet) 
		{
			dbUser = CompilePanel.hosts.user;
			dbPass = CompilePanel.hosts.pass;
		}
			
		ErrorReport.setErrorReportFileName(Globals.CmpErrorLog);
		if(!DBConn.connectionValid(host, dbName,dbUser, dbPass)) {
			System.out.println("Database cannot be found, exiting. ");
			System.out.println(host + " " + dbName + " " + dbUser + " " + dbPass);
			System.out.println("TCW " + Globals.VERSION + " " + Globals.VDATE);
			if (isApplet) return;
			else System.exit(-1);
		}
		//Loads the profile, if none selected creates a default profile
		theSettings = new ViewerSettings(this);

		strDBName = dbName;
		strHostName = host;
		strDBUser = dbUser;
		strDBPass = dbPass;

		setTitle("viewMultiTCW " + Globals.VERSION + ": " + dbName);
		setWindowSettings("viewMultiTCW");
		buildFrame();
	}
	
	public static int getFrameCount () { return openFrames.size();}
	public static void closeAllFrames() {
		if(openFrames != null) {
			for(int x=0; x<openFrames.size(); x++)
				openFrames.get(x).dispose();
		}
	}
	
	public static void closeFrame(int frame) {
		if(openFrames != null) {
			openFrames.get(frame).dispose();
			openFrames.remove(frame);
		}
	}
	
	public String getDBName() { return strDBName; }
	
	public boolean isApplet() { return bIsApplet; }
	
	// WN not sure where to put this to have it run exactly once, so do it this way instead
	private void setBlast() {
		if (blastIsSet) return;
		blastIsSet = true;
		try {
			File hostFile = new File("HOSTS.cfg");
			HostsCfg hosts = new HostsCfg();

		}
		catch(Exception e) {
			System.err.println("Error reading HOSTS.cfg"); 
		}

	}
	private void setWindowSettings(final String prefix) {
		// Load window dimensions from preferences
		Preferences userPrefs = Preferences.userRoot();
		int nX = userPrefs.getInt(prefix + "_frame_win_x", Integer.MAX_VALUE);
		int nY = userPrefs.getInt(prefix + "_frame_win_y", Integer.MAX_VALUE);
		int nWidth = userPrefs.getInt(prefix + "_frame_win_width", Integer.MAX_VALUE);
		int nHeight = userPrefs.getInt(prefix + "_frame_win_height", Integer.MAX_VALUE);
		if (nX == Integer.MAX_VALUE) {
			UIHelpers.centerScreen(this);
		} else
			setBounds(nX, nY, nWidth, nHeight);

		// Setup to save window dimensions when it is closed
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				try {
					// Save the window dimensions to the preferences
					Preferences userPrefs = Preferences.userRoot();
					userPrefs.putInt(prefix + "_frame_win_x", getX());
					userPrefs.putInt(prefix + "_frame_win_y", getY());
					userPrefs.putInt(prefix + "_frame_win_width", getWidth());
					userPrefs.putInt(prefix + "_frame_win_height", getHeight());
					userPrefs.flush();

				} catch (Exception err) {
					ErrorReport.reportError(err, "Error: initializing");
				}
			}
		});
	}
	private void buildSelectFrame() {
		try {
			setTitle("viewMultiTCW " + InetAddress.getLocalHost().getHostName());
			setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

			DatabaseSelectPanel thePanel = new DatabaseSelectPanel(
			        Globals.CMPDBPREFIX, Globals.CMPDBPREFIXold, null);
			mainPanel = new JPanel();
			mainPanel.setLayout( new BoxLayout(mainPanel, BoxLayout.PAGE_AXIS) );
			mainPanel.setBackground(theSettings.getFrameSettings().getBGColor());
			
			mainPanel.add(thePanel);
			mainPanel.setMaximumSize(mainPanel.getPreferredSize());
			
			add(mainPanel);
			setBackground(theSettings.getFrameSettings().getBGColor());
			pack();	
		}
		catch(Exception e) {
			ErrorReport.reportError(e, "Error building frame");
		}
		catch(Error e) {
			ErrorReport.reportFatalError(e, "Fatal error building frame", this);
		}
	}
	
	private void buildFrame() {
		try {
			theSettings.setProfileToDefault();		

			setMinimumSize(new Dimension(theSettings.getFrameSettings().getFrameWidth(), 
					theSettings.getFrameSettings().getFrameHeight()));
			//In case the current JVM doesn't resize from the above
			setSize(new Dimension(theSettings.getFrameSettings().getFrameWidth(), 
					theSettings.getFrameSettings().getFrameHeight()));
			setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

			setMethodData();
			DBinfo sum = new DBinfo(getDBConnection(), false); // only update if needed
			sum.getInfoCol();
			theTaxas = sum.getTaxa();
			theAsms = sum.getASM();
			theSeqLibs = sum.getSeqLib();
			theSeqDEs = sum.getSeqDE();
			thePOGLibs = sum.getPOGLib();
			thePOGDEs = sum.getPOGDE();
			hasNegDE = sum.hasNegDE();
			
			createViewerFrame();
		}
		catch(Exception e) {
			ErrorReport.reportError(e, "Error building frame");
		}
		catch(Error e) {
			ErrorReport.reportFatalError(e, "Fatal error building frame", this);
		}
	}
	
	/*****************************************************
	 * Database methods (why not using DBConn?)
	 */
	public DBConn getDBConnection() throws Exception
	{
		return new DBConn(strHostName, strDBName, strDBUser, strDBPass);
	}
	
	static public ResultSet executeQuery(DBConn conn, String query, JTextField updateField) throws Exception {
		//Wait for resultSet to become available
		double x = 0;
		while(curResult != null) {
			Thread.sleep(100);
			x += .1;
			if(updateField != null) 
				updateField.setText("Waiting for DB to be available (" + x + ") seconds lapsed");
		}
		curResult = conn.executeQuery(query);
		return curResult;
	}
	
	static public void closeResultSet(ResultSet rset) throws Exception {
		rset.close();
		curResult = null;
	}
	
	static public void shutDownFromApplet() {
		if(curResult != null) {
			try {
				curResult.close();
				curResult = null;
			}
			catch(Exception e) {
				
			}
		}
	}
	
	public ViewerSettings getSettings() { return theSettings; }
	
	public void setSelection(JPanel panel) {
		menuPanel.setSelected(panel);
	}
	
	public String getPOGSubQuery() {
		if(pogQueryPanel == null) return "";
		return pogQueryPanel.getSubQuery(); 
	}
	public String getPOGQuerySummary() { 
		if(pogQueryPanel == null) return "Show all";
		return pogQueryPanel.getQuerySummary(); 
	}
	
	public String getUnitransSubQuery() {
		if(unitransQueryPanel == null) return "unitrans.aaSeq IS NOT NULL limit 100000";
		return unitransQueryPanel.getSubQuery();
	}
	
	public String getUnitransQuerySummary() {
		if(unitransQueryPanel == null) return "Show All (limit 100000)";
		return unitransQueryPanel.getQuerySummary();
	}
	
	private void setMethodData() {
		try {
			DBConn conn = getDBConnection();
			ResultSet rset = executeQuery(conn, "SELECT PMid, PMstr, prefix FROM pog_method", null);
			
			Vector<String> names = new Vector<String> ();
			Vector<Integer> ids = new Vector<Integer> ();
			Vector<String> prefixes = new Vector<String> ();
			while(rset.next()) {
				ids.add(rset.getInt(1));
				names.add(rset.getString(2));
				prefixes.add(rset.getString(3));
			}
			closeResultSet(rset);
			
			theMethods = new String[names.size()];
			theMethodIDs = new int[ids.size()];
			theMethodPrefixes = new String[prefixes.size()];
			
			for(int x=0; x<names.size(); x++) {
				theMethods[x] = names.get(x);
				theMethodIDs[x] = ids.get(x);
				theMethodPrefixes[x] = prefixes.get(x);
			}
			
			names.clear();
			ids.clear();
			prefixes.clear();
		}
		catch(Exception e) {
			ErrorReport.prtReport(e, "Error loading method names");
		}
		catch(Error e) {
			ErrorReport.reportFatalError(e, "Fatal error loading method names", this);
		}
	}
	
	public String [] getMethodPrefixes() { return theMethodPrefixes; }
	public String [] getMethodNames() { return theMethods; }
	public int getMethodIDForName(String methodName) {
		for(int x=0; x<theMethods.length; x++) {
			if(theMethods[x].equals(methodName))
				return theMethodIDs[x];
		}
		return -1;
	}

	public String [] getAsmList() { return theAsms; }
	public String [] getTaxaList() { return theTaxas; }
	public String [] getSeqLibList() { return theSeqLibs; }
	public String [] getPOGLibList() { return thePOGLibs; }
	public String [] getSeqDEList() { return theSeqDEs; }
	public String [] getPOGDEList() { return thePOGDEs; }
	public boolean hasNegDE() {return hasNegDE;}
	
	public void addAlignmentTab(ListDataPanel parent, String [] POGMembers, int mode, String cacheFileName) {
		String label;
		if(cacheFileName == null)
			label = "Mixed";
		else
			label = cacheFileName;
		
		final ViewerFrame theFrame = this;
		final ListDataPanel parentCopy = parent;
		final String [] theMembers = POGMembers;
		final int theMode = mode;
		final String thePOGName = label;
		final String fileName = cacheFileName;
		final String summary = parent.getSummary();
		Thread thread = new Thread(new Runnable() {
			public void run() {
				try {
					AlignmentViewPanel newTab = new AlignmentViewPanel(theFrame, theMembers, theMode, fileName);
					String tabName;
					
					if(theMode == PAIR_MODE)
						tabName = "Pairwise " + getNextLabelNum("Pairwise ") + ": " + thePOGName;
					else
						tabName = "Muscle " + getNextLabelNum("Muscle ") + ": " + thePOGName;
					
					addResultPanel(parentCopy, newTab, tabName, summary);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
		thread.setPriority(Thread.MIN_PRIORITY);
		thread.start();

	}
	
	public int getNextLabelNum(String prefix) {
		for(int x=1; true; x++) {
			if(!menuPanel.doesNodeLabelBeginWith(prefix + x))
				return x;
		}
	}

	public void addAlignmentTabFromCache(ListDataPanel parent, String cacheFileName) {
		final ViewerFrame theFrame = this;
		final ListDataPanel theParent = parent;
		final String fileName = cacheFileName;
		final String summary = parent.getSummary();
		
		try {
			AlignmentViewPanel newTab = new AlignmentViewPanel(theFrame,MULTI_MODE, fileName);
			String tabName;
			
			tabName = "Muscle " + getNextLabelNum("Muscle ") + ": " + cacheFileName;
			
			addResultPanel(theParent, newTab, tabName, summary);
		} catch(Exception e) {
			ErrorReport.reportError(e);
		}
	}	

	private String getOverviewText() {
		try {
			DBConn conn = getDBConnection(); 
			ResultSet rset = executeQuery(conn, "SELECT summary FROM info", null);
			rset.next();
			String retVal = rset.getString(1);
			closeResultSet(rset);
			conn.close();
			 
			return retVal;
		} catch (Exception e) {
			ErrorReport.reportError(e, "Error loading overview");
		} catch (Error e) {
			ErrorReport.reportFatalError(e, "Fatal error loading overview", this);
		}
		return null;
	}
	
	private void createMainPanel() {
		//Create main panel
		mainPanel = new JPanel();
		mainPanel.setLayout( new BoxLayout(mainPanel, BoxLayout.PAGE_AXIS) );
		mainPanel.setBackground(theSettings.getFrameSettings().getBGColor());

		menuSelectListener = new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
			}
		};
		
		menuCloseListener = new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				if(arg0.getSource() instanceof JButton) {
					JPanel curPanel = menuPanel.getSelectedPanel();
					JPanel remPanel = menuPanel.getMenuItem((JButton)arg0.getSource());
					
					removePanel(remPanel);
					
					if(curPanel == remPanel)
						menuPanel.setSelected(resultPanel);					
				}
			}
		};
		menuPanel = new MenuPanel(this, menuSelectListener, menuCloseListener);

		//Create individual content panels
		instructionsPanel = new StyleTextPanel("html/viewMultiTCW/Instructions.html");
		overviewPanel = new TextPanel(getOverviewText(), false);
		listAllGroupsPanel = new ListDataPanel(this, MAIN_MENU[2], ListDataPanel.MODE_POG);
		listAllMembersPanel = new ListDataPanel(this, MAIN_MENU[3], ListDataPanel.MODE_UNITRANS);
		pogQueryPanel = new POGQueryPanel(this);
		unitransQueryPanel = new MemberQueryPanel(this);
		resultPanel = new ResultPanel(this, RESULT_COLUMNS);

		menuPanel.addMenuItem(null, instructionsPanel, MAIN_MENU[0], MENU_DESCRIP[0]);
		menuPanel.addMenuItem(null, overviewPanel, MAIN_MENU[1], MENU_DESCRIP[1]);
		menuPanel.addMenuItem(null, listAllGroupsPanel, MAIN_MENU[2], MENU_DESCRIP[2]);
		menuPanel.addMenuItem(null, listAllMembersPanel, MAIN_MENU[3], MENU_DESCRIP[3]);
		menuPanel.addMenuItem(null, pogQueryPanel, MAIN_MENU[4], MENU_DESCRIP[4]);
		menuPanel.addMenuItem(null, unitransQueryPanel, MAIN_MENU[5], MENU_DESCRIP[5]);
		menuPanel.addMenuItem(null, resultPanel, MAIN_MENU[6], MENU_DESCRIP[6]);
		
		//Add all panels to the main
		mainPanel.add(instructionsPanel);
		mainPanel.add(overviewPanel);
		mainPanel.add(listAllGroupsPanel);
		mainPanel.add(listAllMembersPanel);
		mainPanel.add(pogQueryPanel);
		mainPanel.add(unitransQueryPanel);
		mainPanel.add(resultPanel);

		menuPanel.setSelected(overviewPanel);
		
    	sPane = new JScrollPane(menuPanel);
	}
	
	private void createViewerFrame() {
		createMainPanel();

		txtStatus = new JTextField();
		txtStatus.setEditable(false);
		txtStatus.setBorder(BorderFactory.createEmptyBorder());
		txtStatus.setBackground(theSettings.getFrameSettings().getBGColor());
		
		splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
        splitPane.setContinuousLayout(true);
        splitPane.setDividerLocation(theSettings.getFrameSettings().getFrameWidth()/5);

        splitPane.setBorder(null);
        splitPane.setRightComponent(mainPanel);
        splitPane.setLeftComponent(sPane);
        
        setLayout(new BorderLayout());
        add(splitPane, BorderLayout.CENTER);
        add(txtStatus, BorderLayout.SOUTH);
        
        setVisible(false);
        setVisible(true);
	}
	
	public void setStatus(String status) {
		txtStatus.setText(status);
	}

	public void addResultPanel(JPanel parentPanel, JPanel newPanel, String name, String summary) {
		mainPanel.add(newPanel);
		menuPanel.addMenuItem(parentPanel, newPanel, name, summary);
		resultPanel.addResult(newPanel, name, summary);
	}
	
	public void addResultPanel(JPanel newPanel, String name, String summary) {
		mainPanel.add(newPanel);
		menuPanel.addMenuItem(resultPanel, newPanel, name, summary);
		resultPanel.addResult(newPanel, name, summary);
	}
	
	public void removePanel(JPanel panel) {
		mainPanel.remove(panel);
		menuPanel.removeMenuItem(panel);
		resultPanel.removePanel(panel);
	}
	
	public void removePanel(JButton closeButton) {
		removePanel(menuPanel.getMenuItem(closeButton));
	}
	
	public void removePanelFromMenuOnly(JPanel panel) {
		menuPanel.removeMenuItem(panel);
	}
	
	public void changePanelName(JPanel sourcePanel, String newName, String summary) {
		menuPanel.renameMenuItem(sourcePanel, newName);
		resultPanel.renamePanel(sourcePanel, newName, summary);
	}
		
	//Main panels for the window
	private JSplitPane splitPane = null;
	private MenuPanel menuPanel = null;
	private JPanel mainPanel = null;
    private JScrollPane sPane = null;
	
	//Individual content panels
	private StyleTextPanel instructionsPanel = null;//, overviewPanel = null;
	private TextPanel overviewPanel = null;//, overviewPanel = null;
	private ListDataPanel listAllGroupsPanel = null;
	private ListDataPanel listAllMembersPanel = null;
	private POGQueryPanel pogQueryPanel = null;
	private MemberQueryPanel unitransQueryPanel = null;
	private ResultPanel resultPanel = null;
	private CompilePanel compilePanel = null;
	
	private JTextField txtStatus = null;
	
	//Events for selecting/closing menus
	private ActionListener menuSelectListener = null;
	private ActionListener menuCloseListener = null;
		
	//Settings
	private ViewerSettings theSettings = null;
	
	//Result set queue
	private static ResultSet curResult = null;
	
	private boolean bIsApplet = false;
	//Values
	private String strHostName = "";
	private String strDBName = "";
	private String strDBUser = "";
	private String strDBPass = "";
	
	private String [] theMethods = null;
	private int [] theMethodIDs = null;
	private String [] theMethodPrefixes = null;
	
	private String [] theAsms = null;
	private String [] theTaxas = null;
	private String [] theSeqLibs = null;
	private String [] thePOGLibs = null;
	private String [] theSeqDEs = null;
	private String [] thePOGDEs = null;
	private boolean hasNegDE=false;
	
	private CompileMain theParent = null;
	private boolean blastIsSet = false;
	
	public class StyleTextPanel extends JPanel
	{
		private static final long serialVersionUID = 1043240071484012170L;

		public StyleTextPanel ( String strURL )
		{
			if (!strURL.startsWith("/")) strURL = "/" + strURL;
			URL url = null;
			TCWEditorPane editorPane = null;
			try {
				url = StyleTextPanel.class.getResource(strURL);
	
				editorPane = new TCWEditorPane(url);
			}
			catch(Exception e) {
				e.printStackTrace();
		    	url = null;
			}
			if (url == null)
				editorPane.setText("Failed to load the instructions file ");

			JScrollPane scrollPane = new JScrollPane(editorPane);
			scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
			scrollPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
			
			setLayout(new BoxLayout ( this, BoxLayout.X_AXIS ));
			add(scrollPane);
		}
	}
}
